#pragma once
#include "../mqcommon/Log.hpp"
#include "../mqcommon/Helper.hpp"
#include "../mqcommon/Message.pb.h"

#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <atomic>

namespace ouyangmq
{
    struct Binding
    {
        using ptr = std::shared_ptr<Binding>;
        std::string exchange_name;
        std::string Msgqueue_name;
        std::string binding_key;
        bool durable;

        Binding() {}
        Binding(const std::string &ename, const std::string &qname, const std::string &key, bool bdurable)
            : exchange_name(ename), Msgqueue_name(qname), binding_key(key), durable(bdurable)
        {
        }
    };

    // 队列和binding_key的映射关系
    using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
    // 交换机和队列的映射关系
    using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;

    class BindingMapper
    {
    public:
        using ptr = std::shared_ptr<BindingMapper>;
        BindingMapper(const std::string &dbfile)
            : _sqlit_helper(dbfile)
        {
            // 如果指定目录不存在, 创建指定目录
            if (!HelperModule::FileHelper::Exits(dbfile))
                HelperModule::FileHelper::Createfile(dbfile);
            // 打开对应的数据库文件
            assert(_sqlit_helper.open());
            // 创建表
            CreateTable();
        }

        // 创建/删除绑定数据库表
        void CreateTable()
        {
            // create table if not exists binding_table(exchange_name varchar(32), 
            // msgqueue_name varchar(32), binding_key varchar(128), durable int);
            std::stringstream ss;
            ss << "create table if not exists binding_table(";
            ss << "exchange_name varchar(32), ";
            ss << "msgqueue_name varchar(32), ";
            ss << "binding_key varchar(128), ";
            ss << "durable int);";
            assert(_sqlit_helper.exec(ss.str(), nullptr, nullptr));
        }

        void DeleteTable()
        {
            // "drop table if exists binding_table;"
            std::stringstream ss;
            ss << "drop table if exists binding_table;";
            assert(_sqlit_helper.exec(ss.str(), nullptr, nullptr));
        }

        // 新增绑定信息数据/删除指定绑定信息数据
        bool AddBinding(Binding::ptr &binding)
        {
            // "insert into binding_table values('exchange1', 'msgqueue1', 'news.music.#', false/true );"
            std::stringstream ss;
            ss << "insert into binding_table values(";
            ss << "'" << binding->exchange_name << "', ";
            ss << "'" << binding->Msgqueue_name << "', ";
            ss << "'" << binding->binding_key << "', ";
            ss << binding->durable << ");";
            return _sqlit_helper.exec(ss.str(), nullptr, nullptr);
        }

        void RemoveBinding(const std::string &ename, const std::string &qname)
        {
            // delete from binding_table where exchange_name='' and msgqueue_name='';
            std::stringstream ss;
            ss << "delete from binding_table where exchange_name=";
            ss << "'" << ename << "' and ";
            ss << "msgqueue_name=";
            ss << "'" << qname << "';";
            _sqlit_helper.exec(ss.str(), nullptr, nullptr);
        }

        // 移除指定交换机相关绑定信息数据
        void RemoveExchangeBindings(const std::string &ename)
        {
            std::stringstream ss;
            ss << "delete from binding_table where exchange_name=";
            ss << "'" << ename << "';";
            _sqlit_helper.exec(ss.str(), nullptr, nullptr);
        }

        // 移除指定队列相关绑定信息数据
        void RemoveQueueBindings(const std::string &qname)
        {
            std::stringstream ss;
            ss << "delete from binding_table where msgqueue_name=";
            ss << "'" << qname << "';";
            _sqlit_helper.exec(ss.str(), nullptr, nullptr);
        }

        // 查询所有绑定信息数据
        BindingMap Recovery()
        {
            BindingMap result;
            std::string sql = "select * from exchange_table";
            _sqlit_helper.exec(sql, BindingCallback, &result);
            return result;
        }

    private:
        // 必须使用static静态函数，不然参数列表中会含有this参数
        // typedef int (*SqliteCallback)(void *, int, char **, char **);
        static int BindingCallback(void *arg, int col, char **ret, char **fields)
        {
            BindingMap *result = static_cast<BindingMap *>(arg);
            Binding::ptr bp = std::make_shared<Binding>(ret[0], ret[1], ret[2]);
            // 为了防止 交换机相关的绑定信息已经存在，因此不能直接创建队列映射，进行添加，这样会覆盖历史数据
            // 因此得先获取交换机对应的映射对象，往里边添加数据
            // 但是，若这时候没有交换机对应的映射信息，因此这里的获取要使用引用（会保证不存在则自动创建）
            MsgQueueBindingMap &qmap = (*result)[bp->exchange_name];
            qmap.emplace(bp->exchange_name, bp);
            return 0; // 必须返回
        }

    private:
        // 1. sqlit数据库操作句柄
        HelperModule::SqliteHelper _sqlit_helper;
    };

    class BindingManager
    {
    public:
        using ptr = std::shared_ptr<BindingManager>;
        BindingManager(const std::string &dbfile)
            : _mapper(dbfile)
        {
            // 加载持久化的绑定数据
            _bindings = _mapper.Recovery();
        }

        // 绑定
        bool DeclareBinding(const std::string &ename, const std::string &qname, const std::string &key)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if (it != _bindings.end() && it->second.find(qname) != it->second.end())
                return true;

            // 构造一个对象
            // 交换机和队列的映射关系
            // using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
            // using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;
            Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
            auto &qbmap = _bindings[ename];
            // 绑定信息是否持久化取决于交换机持久化和队列持久化
            if(bp->durable == true)
            {
                bool ret = _mapper.AddBinding(bp);
                return ret;
            }
            qbmap.emplace(qname, bp);
            return true;
        }

        // 解绑
        void DeleteBinding(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if (it == _bindings.end() && it->second.find(qname) == it->second.end())
                return;
            _mapper.RemoveBinding(ename, qname);
            auto &qbmap = _bindings[ename];
            qbmap.erase(qname);
        }

        // 删除指定交换机的所有绑定信息 --> 在删除交换机的时候调用
        void RemoveExchangBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveExchangeBindings(ename);
            _bindings.erase(ename);
        }

        // 删除指定队列的所有绑定信息 --> 在删除队列的时候调用
        void RemoveMsgQueueBindings(const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveQueueBindings(qname);
            // 循环遍历_bindings去删除含有qname的映射关系，有就删除，没有就跳过
            for(auto start = _bindings.begin(); start != _bindings.end(); ++start)
            {
                start->second.erase(qname);
            }
        }

        // 获取指定交换机的全部绑定信息
        MsgQueueBindingMap GetExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end())
                return MsgQueueBindingMap();
            return eit->second;
        }

        // 获取一个绑定信息
        Binding::ptr GetOneBinding(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end())
                return Binding::ptr();
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end())
                return Binding::ptr();
            return qit->second;
        }

        bool Exists(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if (it != _bindings.end() && it->second.find(qname) != it->second.end())
                return true;
            return false;
        }

        size_t Size()
        {
            std::atomic<size_t> total;
            for(auto start = _bindings.begin(); start != _bindings.end(); ++start)
            {
                total.fetch_add(start->second.size());
            }
            return total;
        }

        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.DeleteTable();
            _bindings.clear();
        }

    private:
        std::mutex _mutex;
        BindingMapper _mapper;
        BindingMap _bindings;
    };
}